#include <queue>
#include <condition_variable>
#include <mutex>

template <class T>
class BlockQueue
{
public:
   BlockQueue(size_t n = 10)
      :_n(n)
   {}

   void push(const T& task)
   {
      std::unique_lock<std::mutex> ul(_mutex);
      if (_block_queue.size() == _n)
      {
	 _cond_consumer.notify_one();
	 _cond_productor.wait(ul);
      }
      // push
      _block_queue.push(task);
      _cond_consumer.notify_one();
   }

   T pop()
   {
      T ret = T();
      {
	 std::unique_lock<std::mutex> ul(_mutex);
      	 if (_block_queue.empty())
      	 {
      	    _cond_productor.notify_one();
      	    _cond_consumer.wait(ul);
      	 }
      	 ret = _block_queue.front();
      	 _block_queue.pop();
	 _cond_productor.notify_one();
      }
      return ret;
   }

private:
   size_t _n;
   std::queue<T> _block_queue;
   std::mutex _mutex;
   std::condition_variable _cond_productor;
   std::condition_variable _cond_consumer;
};
